package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("ProcessmethodService")
@Transactional

public class ProcessmethodServiceImpl extends CmiEmaService implements ProcessmethodService {
	public ProcessmethodServiceImpl() {
	}

	@Transactional
	public Processmethod saveProcessmethod(Processmethod processmethod) {
		Set<Food> existingFoodsForFood = new HashSet<Food>();

		Set<Food> selectedFoodsForFood = processmethod.getFoodsForProcessmethod();

		if (processmethod.getId() == null) {
			Unit unit = unitDAO.findUnitById(processmethod.getUnit().getId());

			Processmethod existingProcessmethod = processmethodDAO.findProcessmethodByPhysicalPrimaryKey(
					processmethod.getName(), processmethod.getFee(), unit.getId());

			processmethod.setUnit(unit);

			if (isEquals(processmethod, existingProcessmethod)) {
				return existingProcessmethod;
			}

			processmethod = processmethodDAO.store(processmethod);
			processmethodDAO.flush();

			for (Food relatedFood : selectedFoodsForFood) {
				relatedFood.getProcessmethodsForFood().add(processmethod);
				foodDAO.store(relatedFood);
			}
		} else {
			Processmethod existingProcessmethod = processmethodDAO.findProcessmethodByPrimaryKey(processmethod.getId());

			existingFoodsForFood = existingProcessmethod.getFoodsForProcessmethod();

			Set<Food> prepareDeleteFoodsForFood = new HashSet<Food>();
			Set<Food> prepareAddFoodsForFood = new HashSet<Food>();

			prepareDeleteFoodsForFood.addAll(selectedFoodsForFood);
			prepareDeleteFoodsForFood.addAll(existingFoodsForFood);

			prepareAddFoodsForFood.addAll(selectedFoodsForFood);
			prepareAddFoodsForFood.addAll(existingFoodsForFood);

			prepareDeleteFoodsForFood.removeAll(selectedFoodsForFood);
			prepareAddFoodsForFood.removeAll(existingFoodsForFood);

			for (Food relatedFood : prepareAddFoodsForFood) {

				relatedFood.getProcessmethodsForFood().add(existingProcessmethod);
				foodDAO.store(relatedFood);
			}

			for (Food relatedFood : prepareDeleteFoodsForFood) {

				relatedFood.getProcessmethodsForFood().remove(existingProcessmethod);
				foodDAO.store(relatedFood);
			}
			processmethod = processmethodDAO.store(processmethod);
			processmethodDAO.flush();
		}

		foodDAO.flush();
		return processmethod;
	}

	public void deleteProcessmethod(Processmethod processmethod) {
		processmethodDAO.remove(processmethod);
		processmethodDAO.flush();
	}

	@Transactional
	public void deleteProcessmethods(Set<Integer> deleteProcessmethodsId) {
		Query query = processmethodDAO.createNamedQuery("deleteProcessmethodsById", -1, -1);
		query.setParameter(1, deleteProcessmethodsId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Processmethod> loadProcessmethods() {
		return processmethodDAO.findAllProcessmethods();

	}

	@Transactional
	public Set<Processmethod> loadReferenceProcessmethods(Set<Processmethod> processmethods) {
		Iterator<Processmethod> processmethodsIterator = processmethods.iterator();
		Set<Processmethod> referencedProcessmethods = new HashSet<>();
		while (processmethodsIterator.hasNext()) {
			Processmethod currentProcessmethod = processmethodsIterator.next();

			if (!currentProcessmethod.getFoodsForProcessmethod().isEmpty()) {
				referencedProcessmethods.add(currentProcessmethod);
				continue;
			}
		}
		return referencedProcessmethods;
	}

	public Set<Processmethod> loadProcessmethodsByIdSet(Set<Integer> processmethodIdSet) {

		Iterator<Integer> processmethodIdIterator = processmethodIdSet.iterator();
		Set<Processmethod> processmethodSet = new HashSet<>();
		while (processmethodIdIterator.hasNext()) {
			Integer id = processmethodIdIterator.next();
			Processmethod currentProcessmethod = processmethodDAO.findProcessmethodById(id);
			processmethodSet.add(currentProcessmethod);
		}
		return processmethodSet;
	}

	@Transactional
	public List<Processmethod> findAllProcessmethods(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Processmethod>(processmethodDAO.findAllProcessmethods(startResult, maxRows));
	}

	@Transactional
	public Processmethod findProcessmethodByPrimaryKey(Integer id) {
		return processmethodDAO.findProcessmethodByPrimaryKey(id);
	}

	@Transactional
	public Integer countProcessmethods() {
		return ((Long) processmethodDAO.createQuerySingleResult("select count(o) from Processmethod o")
				.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countProcessmethods(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Processmethod.class);
		return ((Long) processmethodDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countProcessmethodsByName(String queryName) {
		Query query = processmethodDAO.createNamedQuery("countProcessmethodsByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countProcessmethodsByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Processmethod.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadProcessmethodObjectReferencedSet(Processmethod processmethod) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();
		if (processmethod.getUnit() != null) {

			Set<Integer> unitIds = new HashSet<Integer>();
			unitIds.add(processmethod.getUnit().getId());
			objectReferencedSetMap.put("unitIds", unitIds);
		}

		Set<Integer> foodsIds = new HashSet<Integer>();
		for (Food food : processmethod.getFoodsForProcessmethod()) {
			foodsIds.add(food.getId());
		}
		objectReferencedSetMap.put("foodsIds", foodsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadProcessmethodClassReferencedSet(Processmethod processmethod) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("units", unitDAO.findAllUnits());
		classReferencedSetMap.put("foods", foodDAO.findAllFoods());

		return classReferencedSetMap;
	}

	public boolean isEquals(Processmethod processmethod, Object obj) {
		if (obj == processmethod)
			return true;
		if (!(obj instanceof Processmethod))
			return false;
		Processmethod comparedProcessmethod = (Processmethod) obj;
		if (!CheckPropertyEqual(processmethod.getName(), comparedProcessmethod.getName())) {
			return false;
		}
		if (!CheckPropertyEqual(processmethod.getFee(), comparedProcessmethod.getFee())) {
			return false;
		}
		if (!CheckPropertyEqual(processmethod.getUnit().getId(), comparedProcessmethod.getUnit().getId())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

